<!-- 所有地方都用到 -->
<!DOCTYPE html>
<html lang=en>
<head><meta name="generator" content="Hexo 3.9.0">
    <!-- so meta -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="HandheldFriendly" content="True">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="description" content="在系统升级重构时，为了保证数据的一致性, 往往伴随着停服, 此期间无法给用户提供服务或只能提供部分服务。同时，为了确保迁移后业务及数据的正确性，迁移后测试工作也要占用不少时间，如此造成的损失是比较大的。">
<meta property="og:type" content="article">
<meta property="og:title" content="系统升级，如何实现不停服的数据迁移和用户切换">
<meta property="og:url" content="http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:description" content="在系统升级重构时，为了保证数据的一致性, 往往伴随着停服, 此期间无法给用户提供服务或只能提供部分服务。同时，为了确保迁移后业务及数据的正确性，迁移后测试工作也要占用不少时间，如此造成的损失是比较大的。">
<meta property="og:locale" content="en">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181925197.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181931068.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181940147.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181948564.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181957938.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182002621.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182007530.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182016726.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182210647.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182219711.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182047408.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182055200.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204212319793.png">
<meta property="og:updated_time" content="2022-04-25T14:45:48.759Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="系统升级，如何实现不停服的数据迁移和用户切换">
<meta name="twitter:description" content="在系统升级重构时，为了保证数据的一致性, 往往伴随着停服, 此期间无法给用户提供服务或只能提供部分服务。同时，为了确保迁移后业务及数据的正确性，迁移后测试工作也要占用不少时间，如此造成的损失是比较大的。">
<meta name="twitter:image" content="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181925197.png">
    
    
        
          
              <link rel="shortcut icon" href="/images/favicon.ico">
          
        
        
          
            <link rel="icon" type="image/png" href="/images/favicon-192x192.png" sizes="192x192">
          
        
        
          
            <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png">
          
        
    
    <!-- title -->
    <title>系统升级，如何实现不停服的数据迁移和用户切换</title>
    <!-- styles -->
    <link rel="stylesheet" href="/css/style.css">
    <!-- persian styles -->
    
      <link rel="stylesheet" href="/css/rtl.css">
    
    <!-- rss -->
    
    
</head>

<body class="max-width mx-auto px3 ltr">

    <!-- 导航栏/目录栏 -->
    <!-- 
      <div id="header-post">
  <a id="menu-icon" href="#"><i class="fas fa-bars fa-lg"></i></a>
  <a id="menu-icon-tablet" href="#"><i class="fas fa-bars fa-lg"></i></a>
  <a id="top-icon-tablet" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" style="display:none;"><i class="fas fa-chevron-up fa-lg"></i></a>
  <span id="menu">
    <span id="nav">
      <ul>
         
          <li><a href="/">Home</a></li>
         
          <li><a href="/search/">Search</a></li>
         
          <li><a href="/about/">About</a></li>
        
      </ul>
    </span>
    <br/>
    <span id="actions">
      <ul>
        
        
        <li><a class="icon" href="/2022/04/18/架构设计/后台架构实战/基于流量回放实现读服务的自动化回归测试/"><i class="fas fa-chevron-right" aria-hidden="true" onmouseover="$('#i-next').toggle();" onmouseout="$('#i-next').toggle();"></i></a></li>
        
        <li><a class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up" aria-hidden="true" onmouseover="$('#i-top').toggle();" onmouseout="$('#i-top').toggle();"></i></a></li>
        <li><a class="icon" href="#"><i class="fas fa-share-alt" aria-hidden="true" onmouseover="$('#i-share').toggle();" onmouseout="$('#i-share').toggle();" onclick="$('#share').toggle();return false;"></i></a></li>
      </ul>
      <span id="i-prev" class="info" style="display:none;">Previous post</span>
      <span id="i-next" class="info" style="display:none;">Next post</span>
      <span id="i-top" class="info" style="display:none;">Back to top</span>
      <span id="i-share" class="info" style="display:none;">Share post</span>
    </span>
    <br/>
    <div id="share" style="display: none">
      <ul>
  <li><a class="icon" href="http://www.facebook.com/sharer.php?u=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/"><i class="fab fa-facebook " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://twitter.com/share?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&text=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-twitter " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.linkedin.com/shareArticle?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-linkedin " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&is_video=false&description=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-pinterest " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=系统升级，如何实现不停服的数据迁移和用户切换&body=Check out this article: http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/"><i class="fas fa-envelope " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://getpocket.com/save?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-get-pocket " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://reddit.com/submit?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-reddit " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.stumbleupon.com/submit?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-stumbleupon " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://digg.com/submit?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-digg " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.tumblr.com/share/link?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&name=系统升级，如何实现不停服的数据迁移和用户切换&description=&lt;p&gt;在系统升级重构时，为了保证数据的一致性, 往往伴随着停服, 此期间无法给用户提供服务或只能提供部分服务。同时，为了确保迁移后业务及数据的正确性，迁移后测试工作也要占用不少时间，如此造成的损失是比较大的。&lt;br&gt;"><i class="fab fa-tumblr " aria-hidden="true"></i></a></li>
</ul>

    </div>
    <div id="toc">
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-升级重构常见的形式"><span class="toc-number">1.</span> <span class="toc-text">1. 升级重构常见的形式</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#1-1-纯代码式的升级重构"><span class="toc-number">1.0.1.</span> <span class="toc-text">1.1 纯代码式的升级重构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-2-包含存储和代码的升级重构"><span class="toc-number">1.0.2.</span> <span class="toc-text">1.2 包含存储和代码的升级重构</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-数据迁移与用户切换"><span class="toc-number">2.</span> <span class="toc-text">2. 数据迁移与用户切换</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-1-纯代码重构的切换"><span class="toc-number">2.0.1.</span> <span class="toc-text">2.1 纯代码重构的切换</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-含存储重构的切换"><span class="toc-number">2.0.2.</span> <span class="toc-text">2.2 含存储重构的切换</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#2-2-1-版本隔离"><span class="toc-number">2.0.2.0.1.</span> <span class="toc-text">2.2.1 版本隔离</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#2-2-2-数据同步"><span class="toc-number">2.0.2.0.2.</span> <span class="toc-text">2.2.2 数据同步</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#2-2-3-自动化对比服务"><span class="toc-number">2.0.2.0.3.</span> <span class="toc-text">2.2.3 自动化对比服务</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#2-2-4-用户切量"><span class="toc-number">2.0.2.0.4.</span> <span class="toc-text">2.2.4 用户切量</span></a></li></ol></li></ol></li></ol></li></ol></li></ol>
    </div>
  </span>
</div>

     -->


    <div class="content index py4">
        
        <!-- 文章详情页 -->
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
  <header>
    
    <h1 class="posttitle" itemprop="name headline">
        系统升级，如何实现不停服的数据迁移和用户切换
    </h1>



    <div class="meta">
      <span class="author" itemprop="author" itemscope itemtype="http://schema.org/Person">
        <span itemprop="name">Hexo</span>
      </span>
      <!-- 
2021-11-04
SpringBoot 传参
2021-11-02
Google Analytics Web Gtag 开始
2021-11-02
Google Analytics Web Gtag 衡量常见的用户互动
-->


    <div class="postdate">
      
        <time datetime="2022-04-17T16:00:22.000Z" itemprop="datePublished">2022-04-18</time>
        
        (Updated: <time datetime="2022-04-25T14:45:48.759Z" itemprop="dateModified">2022-04-25</time>)
        
      
    </div>


      <!-- 在文章的头部显示分类 -->


    <div class="article-category">
        <i class="fas fa-archive"></i>
        <a class="category-link" href="/categories/架构设计/">架构设计</a> › <a class="category-link" href="/categories/架构设计/后台架构实战/">后台架构实战</a>
    </div>


      

    </div>
  </header>
  
  <!--不知道是什么
  

  -->

  <div class="content" itemprop="articleBody">
    <p>在系统升级重构时，为了保证数据的一致性, 往往伴随着停服, 此期间无法给用户提供服务或只能提供部分服务。同时，为了确保迁移后业务及数据的正确性，迁移后测试工作也要占用不少时间，如此造成的损失是比较大的。<br><a id="more"></a></p>
<p>本章将探讨就如何在不停服的情况下进行系统升级重构以及数据迁移。</p>
<h2 id="1-升级重构常见的形式"><a href="#1-升级重构常见的形式" class="headerlink" title="1. 升级重构常见的形式"></a>1. 升级重构常见的形式</h2><p>升级重构有两种常见的形式，一种是纯代码式的升级，另外一种是包含存储和代码的升级。</p>
<h4 id="1-1-纯代码式的升级重构"><a href="#1-1-纯代码式的升级重构" class="headerlink" title="1.1 纯代码式的升级重构"></a>1.1 纯代码式的升级重构</h4><p>纯代码的升级重构是指针对代码中存在的一些历史遗留问题进行修复，比如代码中的慢 SQL、错误的日志打印方式、未开启事务等等。纯代码的升级重构的架构如下图所示。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181925197.png" alt=""></p>
<h4 id="1-2-包含存储和代码的升级重构"><a href="#1-2-包含存储和代码的升级重构" class="headerlink" title="1.2 包含存储和代码的升级重构"></a>1.2 包含存储和代码的升级重构</h4><p>包含存储和代码的升级重构是指在上述纯代码之外，将原有的架构的存储也一起升级。</p>
<p>存储升级有两种形式，第一种形式是将存储的类型进行升级，比如数据库升级为缓存、拥有的读接口从数据库切换至缓存。进行此类存储类型升级的目的是提升微服务的性能，在同样的硬件配置下，缓存要比数据库快至少一个量级。</p>
<p>如下图所示，在升级后，存储从读写都使用数据库的架构，转变为写操作使用数据库、读操作使用缓存的架构。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181931068.png" alt=""></p>
<p>第二种形式是将一个表结构的存储升级为同类型存储的另外一个更加合理的表结构。此类升级常见于系统构建时，为了快速满足业务需求，在时间紧张的情况下，简单快速的设计到不是特别合理的表结构的场景里。比如原有的表结构采用一张宽表，存储了较多的冗余数据，在升级重构时，需要采用更加合理的表结构存储数据，以便未来能够快速响应业务的发展。</p>
<p>这种升级重构的架构如下图所示，升级后原有微服务的读写都将切换至新的表。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181940147.png" alt=""></p>
<h2 id="2-数据迁移与用户切换"><a href="#2-数据迁移与用户切换" class="headerlink" title="2. 数据迁移与用户切换"></a>2. 数据迁移与用户切换</h2><p>在系统升级重构后，便可以进行线上切换了，下文中我们称升级重构前的代码版本为 v1 ，升级重构后的代码版本为 v2 。</p>
<h4 id="2-1-纯代码重构的切换"><a href="#2-1-纯代码重构的切换" class="headerlink" title="2.1 纯代码重构的切换"></a>2.1 纯代码重构的切换</h4><p>纯代码重构的切换比较简单，当上述 v2 通过测试环境和预热环境的测试之后，就可以直接在线上部署，替换原有的 v1。当部署的 v2 出现问题后，直接进行回滚操作。这是最简单粗暴的切换方式，但同时也存在隐患，采用此种方式部署的 v2 如果出现问题，会影响所有的用户，影响面较大。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181948564.png" alt=""></p>
<p>为了降低影响，可以采用灰度的方式，即用 v2 版本的代码切换至一台或者一定比例 v1 版本的机器。比如现在有 100 台机器部署 v1 版本，当 v2 版本测试完成准备上线时，可以先只在 10 台机器上发布 v2 版本的代码。这样即使 v2 版本的代码存在 Bug，也只会影响访问这 10 台机器的用户（即 10% 的线上流量），这样就缩小了影响面。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204181957938.png" alt=""></p>
<p>假设 v2 版本的代码没有发现任何 Bug，此时则可以继续去切换，逐步进行替换。通过此种灰度的方式，既可以做到不停服的重构切换，又可以缩小因此可能带来的线上问题的影响范围。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182002621.png" alt=""></p>
<h4 id="2-2-含存储重构的切换"><a href="#2-2-含存储重构的切换" class="headerlink" title="2.2 含存储重构的切换"></a>2.2 含存储重构的切换</h4><p>与纯代码重构的切换相比，含存储重构的切换多了一个数据迁移的步骤。不管是不同类型的存储重构，还是同类型不同表结构的存储重构，都需要将原有存储中的数据全部迁移至新的存储中。</p>
<p>对于含存储重构的切换，最简单的方法便是停服，在无任何数据写入的情况下进行数据迁移。迁移之后再进行数据对比，对于无误之后，用重构的新版本、新存储对外提供服务。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182007530.png" alt=""></p>
<p>这种方式适合于以下几种场景：一是业务有间断期或者有低峰期，比如企业内网系统下班或者周末期间几乎没有人使用。二是金融资产的业务，这类业务对于正确性要求极高，用户对资产极度敏感，如果资产出现错误，用户是无法容忍的，为了资产的安全无误有时候需要用户容忍停服的升级重构。</p>
<p>对于用户量巨大，且大部分业务场景都需要提供 7x24 小时服务的互联网业务来说，停服的切换方式是不能够被用户所接受的，因此就需要设计一套既不需要停服，又可以完成用户无感知的切换方案。</p>
<p>为了实现升级重构的不停服切换，整体的新版本上线、数据迁移以及用户切量的架构如下图所示，图中左边部分是老版本待重构的服务，其对应的老的数据存储。图中右边部分则部署着升级重构后获得的新版本的服务和对应的新版本的存储，这个存储可以是缓存，也可以是表结构不同的数据库。在图的下方则是数据同步模块，它主要的作用是实时进行数据同步，将老版存储的历史数据、新增的写入、以及更新的数据实时同步至新版本的存储里。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182016726.png" alt=""></p>
<h6 id="2-2-1-版本隔离"><a href="#2-2-1-版本隔离" class="headerlink" title="2.2.1 版本隔离"></a>2.2.1 版本隔离</h6><p>当升级重构后的新版本开发以及测试完成后，便可以进行线上部署了。新版本部署时，可以将新版本服务对外提供的接口变更为一个新的别名，例如 /app/order/create_new_version，这样即使新版本的服务上线部署并直接对外了，也不会引入老版本的流量，从而实现新版本的隔离。</p>
<h6 id="2-2-2-数据同步"><a href="#2-2-2-数据同步" class="headerlink" title="2.2.2 数据同步"></a>2.2.2 数据同步</h6><p>新版本线上部署及隔离后便可以进行数据同步了，数据同步分为历史数据的全量同步和新增数据的实时同步两个部分。</p>
<p>存储重构涉及两个种类，一种是数据库的缓存，另一种是数据库到另外一种异构的表结构数据库里。这里使用场景较多的数据库到缓存的重构群里进行讲解，另一种场景比较类似，后续可以按此方式进行推演。</p>
<p>包含全量同步和增量实时同步的架构如下图所示：</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182210647.png" alt=""></p>
<p>第一步是将历史数据进行一次全量同步，可以对老版本数据库的数据进行分批遍历，思路大致如下：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> 数据 <span class="keyword">from</span> t_table <span class="keyword">where</span> <span class="keyword">id</span> &gt; last_id <span class="keyword">limit</span> 一批次的数量</span><br></pre></td></tr></table></figure>
<p>从数据库遍历读取完数据之后，便会在同步服务模块里按缓存的格式进行数据格式的转换，转换后的数据写入缓存即可。</p>
<p>上述的 SQL 没有停止条件，且老库一直会有数据持续写入。针对这个问题，可以根据当前数据库已有数据量，数据增长的速度以及数据同步的速度，评估在数据同步期间能够产生的数据量，并评估出这个期间最多可能产生的 Id，这个 Id 便是截止ID。由此并将上述 SQL 进行修改为如下：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> 数据 <span class="keyword">from</span> t_table <span class="keyword">where</span> <span class="keyword">id</span> &gt; last_id <span class="keyword">and</span> <span class="keyword">id</span> &lt; 截止<span class="keyword">ID</span> <span class="keyword">limit</span> 一批次的数量</span><br></pre></td></tr></table></figure>
<p>第二步的增量实时同步与全量同步同时启动，增量同步服务通过 binlog 的方式订阅老版本数据库里的数据变更，可以实时获取老版本数据库中的新增和变更的数据。</p>
<p>需要注意的是增量同步需要在全量同步开始前进行 binlog 订阅。如果在全量同步结束后再订阅 binlog 进行增量数据同步，可能会丢失在全量数据同步期间发生变更的数据。比如一张待同步的数据表里有 100 条数据，当同步到第九十条数据时，第八十条数据发生了 <code>update</code> 操作，如果此时还没有开启增量同步，那么这第八十条数据对应的变更就丢失了。为了防止此问题，就需要前置开启增长同步。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182219711.png" alt=""></p>
<p>最后增量同步除了需要订阅 <code>update</code> 和 <code>delete</code> 的操作外，还需要订阅 <code>insert</code> 的操作，因为全量同步在上述截止ID之后的数据便不会再同步了，需要增量同步处理此类数据。</p>
<h6 id="2-2-3-自动化对比服务"><a href="#2-2-3-自动化对比服务" class="headerlink" title="2.2.3 自动化对比服务"></a>2.2.3 自动化对比服务</h6><p>在完成数据迁移之后，并不是立马就能够开始用户切换了，还需要做一件非常重要的事情，那就是进行测试。</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182047408.png" alt=""></p>
<p>为了做好大规模的代码重构与存储的切换，只是靠人工的测试是远远不够的，很容易出现场景遗漏，因此就需要介入自动化的方式进行测试。基于流量回放的自动化回归测试能够在完成全量数据同步后，录制老版本服务的流量并进行自动化测试回归。它的架构如下图示：</p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204182055200.png" alt=""></p>
<p><img src="https://cdn.jsdelivr.net/gh/zhangqinghua/hexo_image/picgo/202204212319793.png" alt=""></p>
<p>在自动化回归中可能会出现的某一类问题需要特殊处理，因为增量同步延迟会导致数据对比不一致。原则上这类问题应该不存在，因为基于 binlog 的主从同步延迟非常小，但如果遇到上述情况，因为增量同步的时间很小，所以我们可以等待几分钟后再次运行对比不一致的回放请求。</p>
<h6 id="2-2-4-用户切量"><a href="#2-2-4-用户切量" class="headerlink" title="2.2.4 用户切量"></a>2.2.4 用户切量</h6><p>在进行数据对比之后，下一步需要落地的便是用户切量了。在进行用户切量时，有几个原则需要遵循：第一，切量不能一刀切，既不能一次将所有的用户全部切换至新版本服务，需要灰度逐步的将用户从老版本切换到新版本服务。第二，在灰度切量时，需要尽早发现问题，而不是等到切量快完成的时候才发现问题。</p>
<p>对于上述的几个要求，在切量的具体落地时，可以从以下几点着手落地。</p>
<p>对影响面小的要求：</p>
<ol>
<li>对用户进行分析排序<br> 首先对于升级重构的系统设计的所有用户进行分析并按等级划分，可以按用户的注册时间、是否为会员等进行划分。如果重构的模块是订单模块，可以将用户按历史以来的订单量、订单金额进行排序。</li>
<li>对用户分为几大批次<br> 在用户按上述的维度排序后，可以将用户分为几大批次。比如将所有用户按排序分为五等分，第一等的用户因为订单量小，订单金额少，可以最先进行切量，这样便满足前提提到的的出问题影响面少的要求。</li>
</ol>
<p>对于在切量时，要尽可能发现问题的要求：</p>
<ol>
<li>对用户再次进行分析和分类<br> 我们知道一个系统对外一般会提供多个功能点，比如用户模块会对外提供用户注册、查询用户基本信息、修改个人签名等功能，可以对第一得分你的用户进行数据分析，分析这些用户里哪些用户使用了较多的系统功能，在分析后可将低等份的用户使用功能的多少进行排序。</li>
<li>使用功能较多的用户排序在前面<br> 在切量时，第一等份里使用功能较多的用户预先进行切量，从而较早发现问题。</li>
</ol>

  </div>
</article>


<!--  -->


        
          <div id="footer-post-container">
  <div id="footer-post">

    <div id="nav-footer" style="display: none">
      <ul>
         
          <li><a href="/">Home</a></li>
         
          <li><a href="/search/">Search</a></li>
         
          <li><a href="/about/">About</a></li>
        
      </ul>
    </div>

    <div id="toc-footer" style="display: none">
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-升级重构常见的形式"><span class="toc-number">1.</span> <span class="toc-text">1. 升级重构常见的形式</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#1-1-纯代码式的升级重构"><span class="toc-number">1.0.1.</span> <span class="toc-text">1.1 纯代码式的升级重构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-2-包含存储和代码的升级重构"><span class="toc-number">1.0.2.</span> <span class="toc-text">1.2 包含存储和代码的升级重构</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-数据迁移与用户切换"><span class="toc-number">2.</span> <span class="toc-text">2. 数据迁移与用户切换</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-1-纯代码重构的切换"><span class="toc-number">2.0.1.</span> <span class="toc-text">2.1 纯代码重构的切换</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-含存储重构的切换"><span class="toc-number">2.0.2.</span> <span class="toc-text">2.2 含存储重构的切换</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#2-2-1-版本隔离"><span class="toc-number">2.0.2.0.1.</span> <span class="toc-text">2.2.1 版本隔离</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#2-2-2-数据同步"><span class="toc-number">2.0.2.0.2.</span> <span class="toc-text">2.2.2 数据同步</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#2-2-3-自动化对比服务"><span class="toc-number">2.0.2.0.3.</span> <span class="toc-text">2.2.3 自动化对比服务</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#2-2-4-用户切量"><span class="toc-number">2.0.2.0.4.</span> <span class="toc-text">2.2.4 用户切量</span></a></li></ol></li></ol></li></ol></li></ol></li></ol>
    </div>

    <div id="share-footer" style="display: none">
      <ul>
  <li><a class="icon" href="http://www.facebook.com/sharer.php?u=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/"><i class="fab fa-facebook fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://twitter.com/share?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&text=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-twitter fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.linkedin.com/shareArticle?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-linkedin fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&is_video=false&description=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-pinterest fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=系统升级，如何实现不停服的数据迁移和用户切换&body=Check out this article: http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/"><i class="fas fa-envelope fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://getpocket.com/save?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-get-pocket fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://reddit.com/submit?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-reddit fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.stumbleupon.com/submit?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-stumbleupon fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://digg.com/submit?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&title=系统升级，如何实现不停服的数据迁移和用户切换"><i class="fab fa-digg fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.tumblr.com/share/link?url=http://yoursite.com/2022/04/18/架构设计/后台架构实战/系统升级，如何实现不停服的数据迁移和用户切换/&name=系统升级，如何实现不停服的数据迁移和用户切换&description=&lt;p&gt;在系统升级重构时，为了保证数据的一致性, 往往伴随着停服, 此期间无法给用户提供服务或只能提供部分服务。同时，为了确保迁移后业务及数据的正确性，迁移后测试工作也要占用不少时间，如此造成的损失是比较大的。&lt;br&gt;"><i class="fab fa-tumblr fa-lg" aria-hidden="true"></i></a></li>
</ul>

    </div>

<!-- 先隐藏底部框 -->
    <!-- <div id="actions-footer">
        <a id="menu" class="icon" href="#" onclick="$('#nav-footer').toggle();return false;"><i class="fas fa-bars fa-lg" aria-hidden="true"></i> Menu</a>
        <a id="toc" class="icon" href="#" onclick="$('#toc-footer').toggle();return false;"><i class="fas fa-list fa-lg" aria-hidden="true"></i> TOC</a>
        <a id="share" class="icon" href="#" onclick="$('#share-footer').toggle();return false;"><i class="fas fa-share-alt fa-lg" aria-hidden="true"></i> Share</a>
        <a id="top" style="display:none" class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up fa-lg" aria-hidden="true"></i> Top</a>
    </div> -->

  </div>
</div>

        
        <!-- <footer id="footer">
  <div class="footer-left">
    Copyright &copy; 2022 John Doe
  </div>
  <div class="footer-right">
    <nav>
      <ul>
        
        <li><a href="/">Home</a></li>
        
        <li><a href="/search/">Search</a></li>
        
        <li><a href="/about/">About</a></li>
        
      </ul>
    </nav>
  </div>
</footer> -->



<!-- 根据页面mathjax变量决定是否加载MathJax数学公式js -->

    </div>
    <!-- styles -->
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
<link rel="stylesheet" href="/lib/justified-gallery/css/justifiedGallery.min.css">

    <!-- jquery -->
<script src="/lib/jquery/jquery.min.js"></script>
<script src="/lib/justified-gallery/js/jquery.justifiedGallery.min.js"></script>
<!-- clipboard -->

  <script src="/lib/clipboard/clipboard.min.js"></script>
  <script type="text/javascript">
  $(function() {
    // copy-btn HTML
    var btn = "<span class=\"btn-copy tooltipped tooltipped-sw\" aria-label=\"Copy to clipboard!\">";
    btn += '<i class="far fa-clone"></i>';
    btn += '</span>'; 
    // mount it!
    $(".highlight .code pre").before(btn);
    var clip = new ClipboardJS('.btn-copy', {
      target: function(trigger) {
        return trigger.nextElementSibling;
      }
    });
    clip.on('success', function(e) {
      e.trigger.setAttribute('aria-label', "Copied!");
      e.clearSelection();
    })
  })
  </script>

<script src="/js/main.js"></script>
<!-- search -->

<!-- Google Analytics -->

<!-- Baidu Analytics -->

<!-- Disqus Comments -->


</body>
</html>
